{% extends "admin42/platformmgr/base.html" %}

{% load admin_utils %}

{% block content_header %}
<ul class="paas-breadcrumb">
    <li><a href="{% url 'admin.front_page' %}">首页</a></li>
    <li><a href="{% url 'admin.runtimes.buildpack.manage' %}">运行时管理</a></li>
    <li class="active">{{ view.name }}</li>
</ul>
{% endblock %}

{% block main_content %}
<div id="slugrunner-list" class="p20">
    <bk-alert class="mb20" title="Slugrunner 是应用运行阶段的基础环境，用于承载构建阶段的产物，组成可以最终运行的容器镜像。"></bk-alert>

    <bk-button theme="primary" class="create-button" @click="handleCreate">
        新建
    </bk-button>

    <!--  数据列表  -->
    <bk-table :data="data" :size="setting.size">
        <bk-table-column label="名称" prop="name" min-width="250">
            <template slot-scope="props">
                <span class="name" :class="props.row.is_hidden ? 'off-shelf' : ''" @click="showDetailDialog(props.row)">$[ props.row.name ]</span>
                <span class="display_name" :class="props.row.is_hidden ? 'off-shelf' : ''">$[ props.row.display_name ]</span>
            </template>
        </bk-table-column>
        <bk-table-column label="描述" prop="description" show-overflow-tooltip="true"
                         v-if="setting.selectedFields.find(item => item.id === 'description') !== undefined">
            <template slot-scope="props">
                <span :class="props.row.is_hidden ? 'off-shelf' : ''">$[ props.row.description ]</span>
            </template>
        </bk-table-column>
        <bk-table-column label="镜像类型" prop="type" :filters="typeFilters" :filter-multiple="true" :filter-method="filterMethod" width="100">
            <template slot-scope="props">
                <span :class="props.row.is_hidden ? 'off-shelf' : ''">$[ props.row.type ]</span>
            </template>
        </bk-table-column>
        <bk-table-column label="镜像" prop="image" show-overflow-tooltip="true" min-width="320">
            <template slot-scope="props">
                <span :class="props.row.is_hidden ? 'off-shelf' : ''">$[ props.row.image + ':' + props.row.tag ]</span>
            </template>
        </bk-table-column>
        <bk-table-column label="是否显示" prop="is_hidden" width="100" :filters="isHiddenFilters" :filter-multiple="false" :filter-method="filterMethod">
            <template slot-scope="props">
                <span :class="props.row.is_hidden ? 'off-shelf' : ''">$[ props.row.is_hidden ? '否' : '是' ] </span>
            </template>
        </bk-table-column>
        <bk-table-column label="是否默认" prop="is_default" width="100" :filters="isDefaultFilters" :filter-multiple="false" :filter-method="filterMethod">
            <template slot-scope="props">
                <span :class="props.row.is_hidden ? 'off-shelf' : ''">$[ props.row.is_default ? '✅' : '❌' ] </span>
            </template>
        </bk-table-column>
        <bk-table-column label="操作" width="80">
            <template slot-scope="props">
                <bk-button class="mr5" theme="primary" text @click="handleEdit(props.row)">编辑</bk-button>
                <bk-button class="mr5" theme="danger" text @click="handleDelete(props.row)">删除</bk-button>
            </template>
        </bk-table-column>
        <bk-table-column type="setting" :tippy-options="{ zIndex: 3000 }">
            <bk-table-setting-content
                :fields="setting.fields"
                :selected="setting.selectedFields"
                :max="setting.max"
                :size="setting.size"
                @setting-change="handleSettingChange">
            </bk-table-setting-content>
        </bk-table-column>
    </bk-table>

    <!--  详情/创建/修改弹窗  -->
    <bk-dialog
        header-position="left"
        v-model="dialog.visible"
        theme="primary" width="1100"
        :confirm-fn="submitDialog"
        @cancel="cancelDialog"
        :mask-close="dialog.type === 'detail'"
        :show-footer="dialog.type !== 'detail'"
    >
        <div slot="header" v-if="dialog.type === 'detail'">Slugrunner 详情</div>
        <div slot="header" v-else>$[ dialog.type === 'create' ? '新建' : '修改' ] Slugrunner</div>
        <bk-form ref="form">
            <bk-container flex :col="32">

                <bk-row>
                    <bk-col :span="16">
                        <bk-form-item label="名称" :required="dialog.type !== 'detail'">
                            <bk-input v-model="dialog.form.name" :readonly="dialog.type === 'detail'"></bk-input>
                        </bk-form-item>
                    </bk-col>
                    <bk-col :span="8">
                        <bk-form-item label="是否默认">
                            <bk-switcher v-model="dialog.form.is_default" :disabled="dialog.type === 'detail'"></bk-switcher>
                        </bk-form-item>
                    </bk-col>
                    <bk-col :span="8">
                        <bk-form-item label="是否隐藏">
                            <bk-switcher v-model="dialog.form.is_hidden" :disabled="dialog.type === 'detail'"></bk-switcher>
                        </bk-form-item>
                    </bk-col>
                </bk-row>

                <bk-row>
                    <bk-col :span="16">
                        <bk-form-item label="镜像类型" :required="dialog.type !== 'detail'">
                            <bk-input v-model="dialog.form.type" :readonly="true" v-if="dialog.type === 'detail'"></bk-input>
                            <bk-select v-model="dialog.form.type" v-else>
                                <bk-option v-for="type in image_types" :key="type" :id="type" :name="type"></bk-option>
                            </bk-select>
                        </bk-form-item>
                    </bk-col>
                </bk-row>

                <bk-row>
                    <bk-col :span="16">
                        <bk-form-item label="镜像" :required="dialog.type !== 'detail'">
                            <bk-input v-model="dialog.form.image" v-bk-tooltips="dialog.form.image" :readonly="dialog.type === 'detail'"></bk-input>
                        </bk-form-item>
                    </bk-col>
                    <bk-col :span="16">
                        <bk-form-item label="标签" :required="dialog.type !== 'detail'">
                            <bk-input v-model="dialog.form.tag" :readonly="dialog.type === 'detail'"></bk-input>
                        </bk-form-item>
                    </bk-col>
                </bk-row>

                <bk-row>
                    <bk-col :span="16">
                        <bk-form-item label="展示名称 / 中文">
                            <bk-input v-model="dialog.form.display_name_zh_cn" :readonly="dialog.type === 'detail'"
                                      placeholder=" "></bk-input>
                        </bk-form-item>
                    </bk-col>
                    <bk-col :span="16">
                        <bk-form-item label="展示名称 / 英文">
                            <bk-input v-model="dialog.form.display_name_en" :readonly="dialog.type === 'detail'"
                                      placeholder=" "></bk-input>
                        </bk-form-item>
                    </bk-col>
                </bk-row>

                <bk-row>
                    <bk-col :span="16">
                        <bk-form-item label="描述 / 中文">
                            <bk-input v-model="dialog.form.description_zh_cn" :readonly="dialog.type === 'detail'"
                                      placeholder=" "></bk-input>
                        </bk-form-item>
                    </bk-col>
                    <bk-col :span="16">
                        <bk-form-item label="描述 / 英文">
                            <bk-input v-model="dialog.form.description_en" :readonly="dialog.type === 'detail'"
                                      placeholder=" "></bk-input>
                        </bk-form-item>
                    </bk-col>
                </bk-row>

                <bk-row>
                    <bk-col :span="32">
                        <bk-form-item label="环境变量"></bk-form-item>
                    </bk-col>
                </bk-row>
                <bk-row>
                    <bk-col :span="2"></bk-col>
                    <bk-table :data="envList" style="width: 967px">
                        <template slot="empty">
                            <div class="empty-text">暂无数据</div>
                        </template>
                        <!-- 新建环境变量 -->
                        <template slot="append" v-if="dialog.type!=='detail'">
                            <div class="add-wrapper">
                                <span class="add-single-variable">
                                    <bk-link theme="primary" icon="bk-icon icon-plus" @click="handleAddSingleVariable(envList)">添加环境变量</bk-link>
                                </span>
                            </div>
                        </template>

                        <bk-table-column label="Key" :width="dialog.type === 'detail' ? '383px' : '333px'">
                            <template slot-scope="props">
                                <bk-input v-model="props.row.key" v-if="props.row.isEdit"></bk-input>
                                <span v-else>$[ props.row.key ]</span>
                            </template>
                        </bk-table-column>

                        <bk-table-column label="Value" :width="dialog.type === 'detail' ? '583px' : '533px'">
                            <template slot-scope="props">
                                <bk-input v-model="props.row.value" v-if="props.row.isEdit"></bk-input>
                                <span v-else v-bk-tooltips="props.row.value">$[ props.row.value ]</span>
                            </template>
                        </bk-table-column>

                        <bk-table-column label="操作" v-if="dialog.type!=='detail'" width="100px">
                            <template slot-scope="props">
                                <bk-button class="mr5" theme="primary" text @click="handleEditVar(props.row, envList, oldEnvList)"
                                           v-if="props.row.isEdit">保存
                                </bk-button>
                                <bk-button class="mr5" theme="primary" text @click="handleEditVar(props.row, envList, oldEnvList)" v-else>
                                    编辑
                                </bk-button>
                                <bk-button class="mr5" theme="primary" text @click="handleCancelVar(props.row, envList, oldEnvList)"
                                           v-if="props.row.isEdit">取消
                                </bk-button>
                                <bk-button class="mr5" theme="danger" text @click="handleDeleteVar(props.row, envList)" v-else>
                                    删除
                                </bk-button>
                            </template>
                        </bk-table-column>

                    </bk-table>
                </bk-row>

                <bk-row>
                    <bk-col :span="32">
                        <bk-form-item label="镜像标记"></bk-form-item>
                    </bk-col>
                </bk-row>
                <bk-row>
                    <bk-col :span="2"></bk-col>
                    <bk-table :data="labelList" style="width: 967px">
                        <template slot="empty">
                            <div class="empty-text">暂无数据</div>
                        </template>
                        <!-- 新建镜像标记 -->
                        <template slot="append" v-if="dialog.type!=='detail'">
                            <div class="add-wrapper">
                                <span class="add-single-variable">
                                    <bk-link theme="primary" icon="bk-icon icon-plus" @click="handleAddSingleVariable(labelList)">添加镜像标记</bk-link>
                                </span>
                            </div>
                        </template>

                        <bk-table-column label="Key" :width="dialog.type === 'detail' ? '383px' : '333px'">
                            <template slot-scope="props">
                                <bk-input v-model="props.row.key" v-if="props.row.isEdit"></bk-input>
                                <span v-else>$[ props.row.key ]</span>
                            </template>
                        </bk-table-column>

                        <bk-table-column label="Value" :width="dialog.type === 'detail' ? '583px' : '533px'">
                            <template slot-scope="props">
                                <bk-input v-model="props.row.value" v-if="props.row.isEdit"></bk-input>
                                <span v-else v-bk-tooltips="props.row.value">$[ props.row.value ]</span>
                            </template>
                        </bk-table-column>

                        <bk-table-column label="操作" v-if="dialog.type!=='detail'">
                            <template slot-scope="props">
                                <bk-button class="mr5" theme="primary" text @click="handleEditVar(props.row, labelList, oldLabelList)"
                                           v-if="props.row.isEdit">保存
                                </bk-button>
                                <bk-button class="mr5" theme="primary" text @click="handleEditVar(props.row, labelList, oldLabelList)" v-else>
                                    编辑
                                </bk-button>
                                <bk-button class="mr5" theme="primary" text @click="handleCancelVar(props.row, labelList, oldLabelList)"
                                           v-if="props.row.isEdit">取消
                                </bk-button>
                                <bk-button class="mr5" theme="danger" text @click="handleDeleteVar(props.row, labelList)" v-else>
                                    删除
                                </bk-button>
                            </template>
                        </bk-table-column>

                    </bk-table>
                </bk-row>
            </bk-container>
        </bk-form>
    </bk-dialog>
</div>
{% endblock %}

{% block main_script %}
<script>
    const URLRouter = {
        create: decodeURI("{% url 'admin.runtimes.slugrunner' %}"),
        list: decodeURI("{% url 'admin.runtimes.slugrunner' %}"),
        detail: decodeURI("{% url 'admin.runtimes.slugrunner.detail' '${id}' %}"),
    }

    const image_types = {{ image_types | to_json }}

    let typeFilters = []
    for (let type in image_types) {
        typeFilters.push({
            text: type,
            value: type,
        });
    }

    const settingFields = [
        {
            id: 'name',
            label: '名称',
            disabled: true,
        },
        {
            id: 'type',
            label: '镜像类型',
            disabled: true,
        },
        {
            id: 'image',
            label: '镜像',
            disabled: true,
        },
        {
            id: 'description',
            label: '描述',
        }
    ]

    document.addEventListener('DOMContentLoaded', () => {
        new Vue({
            el: "#slugrunner-list",
            delimiters: ['$[', ']'],
            data: function () {
                return {
                    data: [],
                    dialog: {
                        visible: false,
                        type: '',
                        form: {
                            key: '',
                            value: '',
                            description: '',
                        },
                        row: undefined
                    },
                    envList: [],
                    labelList: [],
                    // oldEnvList, oldLabelList 记录修改前的数据，用于取消操作恢复数据
                    oldEnvList: [],
                    oldLabelList: [],
                    image_types: image_types,
                    setting: {
                        max: 3,
                        fields: settingFields,
                        selectedFields: settingFields,
                        size: 'medium'
                    },
                    typeFilters: typeFilters,
                    isHiddenFilters: [
                        {
                            text: '是',
                            value: false,
                        },
                        {
                            text: '否',
                            value: true,
                        }
                    ],
                    isDefaultFilters: [
                        {
                            text: '是',
                            value: true,
                        },
                        {
                            text: '否',
                            value: false,
                        },
                    ],
                }
            },
            methods: {
                fetchSlugrunnerList: async function () {
                    const el = this.$bkLoading({title: '加载中'});
                    try {
                        let url = URLRouter.list;
                        await this.$http.get(url).then(res => {
                            this.data = res;
                        })
                    } catch (e) {
                        if (e.response.status === 400) {
                            this.$bkMessage({
                                theme: 'error',
                                message: e.response.data.detail,
                            })
                        }
                    } finally {
                        el.hide = true;
                    }
                },
                showDetailDialog: async function (row) {
                    this.dialog.row = row;
                    this.dialog.form = {...row};
                    this.dialog.type = 'detail';
                    this.envList = this.jsonToList(this.dialog.form.env_vars || {});
                    this.labelList = this.jsonToList(this.dialog.form.labels || {});
                    this.$nextTick(() => {
                        // Clear any previous errors
                        this.$refs.form.clearError();
                    });
                    this.dialog.visible = true
                },
                cancelDialog: function () {
                    this.dialog.visible = false
                },
                submitDialog: async function () {
                    if (this.dialog.type === 'detail') {
                        this.cancelDialog();
                        return;
                    }
                    this.dialog.form.env_vars = this.ListToJson(this.envList);
                    this.dialog.form.labels = this.ListToJson(this.labelList);
                    const url = this.dialog.type === 'create' ? URLRouter.create : this.fillUrlTemplate(URLRouter.detail, {row: this.dialog.row});
                    let success = true;
                    const method = this.dialog.type === 'create' ? 'post' : 'put';
                    try {
                        await this.$http[method](url, this.dialog.form);
                    } catch (e) {
                        success = false;
                        if (e.response.status === 400) {
                            this.$bkMessage({
                                theme: 'error',
                                message: e.response.data.detail,
                            })
                        }
                    }
                    if (success) {
                        this.cancelDialog();
                    }
                },
                handleCreate: function () {
                    this.dialog.type = "create";
                    this.dialog.row = undefined;
                    this.dialog.form = {
                        key: '',
                        value: '',
                        description: '',
                    };
                    this.envList = [];
                    this.labelList = [];
                    this.$nextTick(() => {
                        // Clear any previous errors
                        this.$refs.form.clearError();
                    });
                    this.dialog.visible = true;
                },
                handleEdit: function (row) {
                    this.dialog.type = "edit";
                    this.dialog.row = row;
                    this.dialog.form = {...row};
                    this.envList = this.jsonToList(this.dialog.form.env_vars || {});
                    this.labelList = this.jsonToList(this.dialog.form.labels || {});
                    this.$nextTick(() => {
                        // Clear any previous errors
                        this.$refs.form.clearError();
                    });
                    this.dialog.visible = true;
                },
                handleDelete: function (row) {
                    this.$bkInfo({
                        title: `确定要删除 ${row.name}？`,
                        confirmLoading: true,
                        theme: 'danger',
                        confirmFn: async () => {
                            try {
                                await this.deleteRow(row)
                                this.$bkMessage({
                                    theme: 'success',
                                    message: '删除成功',
                                });
                            } catch (e) {
                                this.$bkMessage({
                                    theme: 'error',
                                    message: e.response.data.detail,
                                });
                            }
                        }
                    })
                },
                deleteRow: async function (row) {
                    const url = this.fillUrlTemplate(URLRouter.detail, {row});
                    await this.$http.delete(url);
                },
                fillUrlTemplate: function (url_template, {row}) {
                    if (!row) {
                        row = {}
                    }
                    return url_template.replace("${id}", row.id)
                },
                jsonToList: function (json) {
                    let list = [];
                    for (let key in json) {
                        if (json.hasOwnProperty(key)) {
                            list.push({key: key, value: json[key], isEdit: false, isAdd: false});
                        }
                    }
                    return list;
                },
                ListToJson: function (list) {
                    let json = {};
                    list.forEach(item => {
                        json[item.key] = item.value;
                    });
                    return json;
                },
                // 单独新增一个环境变量
                handleAddSingleVariable: function (varList) {
                    varList.push({key: '', value: '', isEdit: true, isAdd: true});
                },
                handleEditVar: function (row, varList, oldList) {
                    if (row.isEdit) {
                        if (row.key === '') {
                            this.$bkMessage({
                                theme: 'error',
                                message: '变量名不能为空',
                            })
                        } else if (row.value === '') {
                            this.$bkMessage({
                                theme: 'error',
                                message: '变量值不能为空',
                            })
                        } else {
                            row.isEdit = !row.isEdit;
                            row.isAdd = false;
                        }
                    } else {
                        const index = varList.findIndex(item => item.key === row.key);
                        oldList[index] = {...row};
                        row.isEdit = !row.isEdit;
                    }
                },
                handleDeleteVar: function (row, varList) {
                    const index = varList.findIndex(item => item.key === row.key);
                    if (index !== -1) {
                        varList.splice(index, 1);
                    }
                },
                handleCancelVar: function (row, varList, oldList) {
                    if (row.isAdd) {
                        this.handleDeleteVar(row, varList);
                    } else {
                        const index = varList.findIndex(item => item.key === row.key);
                        if (index !== -1) {
                            row.key = oldList[index].key;
                            row.value = oldList[index].value;
                            row.isEdit = false;
                        }
                    }
                },
                handleSettingChange ({ fields, size }) {
                    this.setting.size = size;
                    this.setting.selectedFields = fields;
                },
                filterMethod (value, row, column) {
                    const property = column.property
                    return row[property] === value
                }
            },
            mounted: async function () {
                await this.fetchSlugrunnerList();
            },
        })
    })
</script>
<style>
    .bk-grid-row + .bk-grid-row {
        margin-top: 20px;
    }

    .display_name {
        display: flex;
    }

    .name {
        display: flex;
        font-weight: 700;
        color: #3A84FF;
        cursor: pointer;
    }

    .create-button {
        display: flex;
        float: left;
        margin-bottom: 20px;
    }

    .filter-wrapper {
        display: flex;
        float: left;
        margin-left: 10px;
        width: auto;

        .filter-item {
            font-weight: normal;
        }
    }

    .off-shelf {
        color: #c4c6cc;
    }

    .add-wrapper {
        height: 42px;

        .add-single-variable {
            display: inline-block;
            height: 42px;
            line-height: 42px;
            padding: 0 15px;
            color: #3a84ff;
            cursor: pointer;
        }
    }

    .bk-checkbox-text {
        font-weight: normal;
    }

    .empty-text {
        font-size: 14px;
    }
</style>
{% endblock %}
